package com.itdct.onflow.generator.core

import cn.hutool.core.io.FileUtil
import com.alibaba.fastjson2.JSON
import com.alibaba.fastjson2.annotation.JSONField
import com.itdct.onflow.core.entity.IdEntity
import com.itdct.onflow.core.extend.logError
import com.itdct.onflow.core.extend.logInfo
import com.itdct.onflow.core.util.TimeUtil
import com.itdct.onflow.generator.base.BaseTemplate
import com.itdct.onflow.generator.config.GeneratorConfig
import com.itdct.onflow.generator.constant.GroupEnum
import com.itdct.onflow.generator.context.GeneratorContext
import com.itdct.onflow.generator.model.ClassModel
import com.itdct.onflow.generator.model.CompareResultModel
import com.itdct.onflow.generator.model.FieldModel
import java.io.File
import java.nio.charset.Charset
import java.sql.Connection
import java.util.*
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executors

/**
 * @author Zhouwx
 * @date 2025/8/11 11:41:42
 * @version 1.0
 * @description
 */
open class CodeGenerator(
    open val generatorConfig: GeneratorConfig,
    val templateClasses: List<Class<out BaseTemplate>>
) {
    val dbConnector: DbConnector by lazy { DbConnector(generatorConfig) }
    val connection: Connection by lazy { dbConnector.getConnection() }
    val executorService = Executors.newVirtualThreadPerTaskExecutor()
    var generateTime = ""

    fun generates(vararg classList: Class<*>) {
        generateTime = TimeUtil.dateToTimeString(Date(), "yyyy-MM-dd HH.mm.ss")
        val countDownLatch = CountDownLatch(classList.size)
        for (clazz in classList) {
            executorService.submit {
                try {
                    generate(clazz as Class<IdEntity>)
                } catch (e: Exception) {
                    logError("模板生成出现问题", e)
                } finally {
                    countDownLatch.countDown()
                }
            }
        }
        try {
            countDownLatch.await()
        } catch (e: InterruptedException) {
            logError("模板生成出现问题", e)
        }
        connection.close()
    }

    fun generates(vararg classModels: ClassModel) {
        generateTime = TimeUtil.dateToTimeString(Date(), "yyyy-MM-dd HH.mm.ss")
        val countDownLatch = CountDownLatch(classModels.size)
        for (classModel in classModels) {
            executorService.submit {
                try {
                    generate<IdEntity>(classModel)
                } catch (e: Exception) {
                    logError("模板生成出现问题", e)
                } finally {
                    countDownLatch.countDown()
                }
            }
        }
        try {
            countDownLatch.await()
        } catch (e: InterruptedException) {
            logError("模板生成出现问题", e)
        }
        connection.close()
    }

    open fun <T : IdEntity> generate(clazz: Class<T>) {
        val entityAnalyse = EntityAnalyse<T>(generatorConfig)
        val classModel = entityAnalyse.analyseClass(clazz)
        generate<T>(classModel)
    }

    open fun <T : IdEntity> generate(classModel: ClassModel) {
        val context = GeneratorContext()
        context.connection = connection
        context.generateTime = TimeUtil.timestampToString(System.currentTimeMillis(), TimeUtil.yyyyMMddHHmmss_HYPHEN)
        context.classModel = classModel

        // INFO: Zhouwx: 2025/8/15 写入并获取该Entity文件的uuid
        writeEntityFileUuid(context)

        val logFilePath = getLogFilePath(classModel)
        context.logFilePath = logFilePath

        // INFO: Zhouwx: 2025/8/19 当前类和log文件的中类进行比较
        compareClassModel(context)

        for (templateClass in templateClasses) {
            generateTemplateFile(templateClass, context)
        }

        // INFO: Zhouwx: 2025/8/15 同步表结构
        syncTable(context)

        // INFO: Zhouwx: 2025/8/15 写生成日志到本地
        writeLog(context)
    }

    /**
     * 将当前classModel与日志中的classModel进行比较
     */
    fun compareClassModel(context: GeneratorContext) {
        if (context.firstGenerate) {
            context.overwrite = true
            return
        }

        if (generatorConfig.forceOverwrite) {
            context.overwrite = true
            return
        }
        // INFO: Zhouwx: 2025/8/15 将日志的最后一行信息和当前的classModel进行比较
        val logFilePath = context.logFilePath
        val logFile = File(logFilePath)
        if (!logFile.exists()) {
            context.existLogFile = false
            return
        }
        context.existLogFile = true

        val logContent = FileUtil.readString(logFile, Charset.defaultCharset())
        val lines = logContent.split("\n").filter { it.isNotBlank() }
        if (lines.size == 0) {
            context.firstGenerate = true
            context.overwrite = true
            return
        }

        val lastLine = lines.last()
        // INFO: Zhouwx: 2025/9/5 加载日志中最后一次生成的classModel
        val lastClassModel = JSON.parseObject(lastLine, ClassModel::class.java)
        context.lastClassModel = lastClassModel
        for (fieldModel in lastClassModel.fieldList) {
            var className = fieldModel.className
            if (className.isNotBlank()) {
                className = baseTypeToPackageType(className)
                try {
                    val clazz = Class.forName(className)
                    fieldModel.clazz = clazz
                } catch (e: Exception) {
                    logInfo("字段${fieldModel.name}对应的类${className}不存在")
                }
            }
        }
        val classModel = context.classModel
        val originFieldList = lastClassModel.fieldList
        val nowFieldList = classModel.fieldList

        val compareResultModel = CompareResultModel()
        val addFields = ArrayList<FieldModel>()
        val delFields = ArrayList<FieldModel>()
        val modifyFields = ArrayList<Pair<FieldModel, FieldModel>>()
        for (nowFieldModel in nowFieldList) {
            val name = nowFieldModel.name
            val filter = originFieldList.filter { it.name == name }
            if (filter.size == 0) {
                // INFO: Zhouwx: 2024/12/25 新增字段
                context.haveChange = true
                addFields.add(nowFieldModel)
            } else {
                // INFO: Zhouwx: 2024/12/25 修改字段
                val originFieldModel = filter.first()
                val fieldModelClass = FieldModel::class.java
                val allFields = fieldModelClass.getDeclaredFields()
                for (field in allFields) {
                    // INFO: Zhouwx: 2025/8/19 遍历所有fieldModel中的字段
                    field.isAccessible = true
                    val jsonField = field.getAnnotation(JSONField::class.java)
                    if (jsonField != null && !jsonField.serialize) {
                        // INFO: Zhouwx: 2025/8/19 跳过JSONField(serialize = false)的字段
                        continue
                    }
                    if (field.type == ArrayList::class.java) {
                        // INFO: Zhouwx: 2025/8/19 排除列表影响
                        continue
                    }
                    val fieldName = field.name
                    if (fieldName == "forSelect") {
                        // INFO: Zhouwx: 2025/8/19 forSelect不做判断，后面单独判断
                        continue
                    }

                    if (fieldName == "targetClassName" || fieldName == "targetEntityClassName" || fieldName == "targetWholeClassName") {
                        // INFO: Zhouwx: 2025/9/17 目标类变更不做判断，后面单独判断
                        continue
                    }

                    val originFieldValue = field.get(originFieldModel)
                    val nowFieldValue = field.get(nowFieldModel)
                    if (originFieldValue != nowFieldValue) {
                        logInfo("字段名：${name}，字段${fieldName}发生修改，修改前：${originFieldValue}，修改后：${nowFieldValue}")
                        modifyFields.add(Pair(originFieldModel, nowFieldModel))
                        context.haveChange = true
                        break
                    }
                }

                if (nowFieldModel.forSelect != originFieldModel.forSelect) {
                    context.haveChange = true
                    // INFO: Zhouwx: 2025/8/19 forSelect修改，需要特殊处理
                    if (nowFieldModel.forSelect) {
                        // INFO: Zhouwx: 2025/8/19 原来为false，现在为true，说明加上了forSelect
                        compareResultModel.addForSelect.add(nowFieldModel)
                    } else {
                        // INFO: Zhouwx: 2025/8/19 原来为true，现在为false，说明删除了forSelect
                        compareResultModel.delForSelect.add(originFieldModel)
                    }
                }

                if (nowFieldModel.targetWholeClassName != originFieldModel.targetWholeClassName) {
                    context.haveChange = true
                    if (originFieldModel.targetWholeClassName == null) {
                        // INFO: Zhouwx: 2025/9/17 原来是空的，现在不是空的，说明是新增的targetClass
                        compareResultModel.addTargetFields.add(nowFieldModel)
                    } else if (nowFieldModel.targetWholeClassName == null) {
                        // INFO: Zhouwx: 2025/9/17 现在是空的，原来不是空的，说明是删除的targetClass
                        compareResultModel.delTargetFields.add(originFieldModel)
                    } else {
                        logInfo("字段名：${name}，targetWholeClassName发生修改，修改前：${originFieldModel.targetWholeClassName}，修改后：${nowFieldModel.targetWholeClassName}")
                        compareResultModel.modifyTargetFields.add(Pair(originFieldModel, nowFieldModel))
                    }
                }

                if (nowFieldModel.dictCode != originFieldModel.dictCode) {
                    if (originFieldModel.dictCode.isBlank()) {
                        // INFO: Zhouwx: 2025/9/3 原来是空的，现在不是空的，说明是新增的dict
                        compareResultModel.addDict.add(nowFieldModel)
                    } else if (nowFieldModel.dictCode.isBlank()) {
                        // INFO: Zhouwx: 2025/9/3 现在是空的，原来不是空的，说明是删除的dict
                        compareResultModel.delDict.add(originFieldModel)
                    } else if (nowFieldModel.dictCode != originFieldModel.dictCode) {
                        logInfo("字段名：${name}，dictCode发生修改，修改前：${originFieldModel.dictCode}，修改后：${nowFieldModel.dictCode}")
                        compareResultModel.modifyDict.add(Pair(originFieldModel, nowFieldModel))
                    }
                }
            }
        }

        for (originFieldModel in originFieldList) {
            val name = originFieldModel.name
            val filter = nowFieldList.filter { it.name == name }
            if (filter.size == 0) {
                // INFO: Zhouwx: 2024/12/25 删除字段
                delFields.add(originFieldModel)
                context.haveChange = true
            }
        }

        compareResultModel.classModel = classModel
        compareResultModel.addFields = addFields
        compareResultModel.delFields = delFields
        compareResultModel.modifyPairs = modifyFields
        context.compareResultModel = compareResultModel
    }

    fun generateTemplateFile(templateClass: Class<out BaseTemplate>, context: GeneratorContext) {
        if (!generatorConfig.generateFile) {
            return
        }

        val classModel = context.classModel
        val constructor = templateClass.getConstructor(ClassModel::class.java, GeneratorConfig::class.java)
        val template = constructor.newInstance(classModel, generatorConfig)
        val belongGroup = template.belongGroup()
        var rootPath = generatorConfig.groupModulePath[belongGroup] ?: throw Exception("请配置${belongGroup}的模块路径")
        rootPath = rootPath.replace("\\", "/")
        if (!rootPath.endsWith("/")) {
            rootPath += "/"
        }
        val split = rootPath.split("/")
        val moduleName = split[split.size - 2]
        val packageName = classModel.packageName
        val packagePath = packageName.replace(".", "/")
        var templatePath = ""
        when (belongGroup) {
            GroupEnum.BACKEND -> {
                templatePath = "${rootPath}${moduleName}-${template.subModuleName()}/src/main/kotlin/${packagePath}/${template.getFolderFileName()}"
            }

            GroupEnum.MAPPER_XML -> {
                templatePath = "${rootPath}${moduleName}-${template.subModuleName()}/src/main/resources/mapper/${template.getFolderFileName()}"
            }

            GroupEnum.FRONTEND -> {
                templatePath = "${rootPath}src/${template.subModuleName()}/${template.getFolderFileName()}"
            }

            else -> {
                throw Exception("请配置${belongGroup}最终模块路径")
            }
        }

        val templateFile = File(templatePath)
        if (!templateFile.parentFile.exists()) {
            templateFile.parentFile.mkdirs()
        }
        if (!templateFile.exists()) {
            templateFile.createNewFile()
        } else {
            if (context.overwrite) {
                logInfo("文件已存在，开始强制更新")
                backupFile(context, templateFile)
                // INFO: Zhouwx: 2025/8/15 如果强制更新，则文件删除后重新生成
                templateFile.delete()
                templateFile.createNewFile()
            } else {
                // INFO: Zhouwx: 2025/8/15 如果文件存在，则需要更新
                if (context.existLogFile) {
                    // INFO: Zhouwx: 2025/8/15 存在log文件才进行更新
                    val compareResultModel = context.compareResultModel
                    if (compareResultModel != null) {
//                        logInfo("开始增量更新")
                        if (context.haveChange) {
                            backupFile(context, templateFile)
                        }
                        var modifyContent = template.onModify(compareResultModel, templateFile.readText())
                        modifyContent = normalizeEmptyLines(modifyContent)
                        templateFile.writeText(modifyContent)
                    }
                    return
                } else {
                    // INFO: Zhouwx: 2025/8/15 如果没有日志文件，则不能盲目覆盖，直接返回
                    logInfo("文件已存在，且日志文件不存在，跳过覆盖生成")
                    return
                }
            }
        }
        logInfo("开始生成${template.belongGroup()}的${template.subModuleName()}模版文件：${templatePath}")
        var templateContent = template.render()
        templateContent = normalizeEmptyLines(templateContent)
        templateFile.writeText(templateContent)
    }

    private fun backupFile(context: GeneratorContext, templateFile: File) {
        var backupPath = generatorConfig.backupPath
        backupPath = backupPath.replace("\\", "/")
        if (!backupPath.endsWith("/")) {
            backupPath += "/"
        }

        val sessionVersion = generatorConfig.sessionVersion
        val backupFilePath = backupPath + sessionVersion + "/" + generateTime + "/" + context.classModel.className + "/" + templateFile.name
        FileUtil.copyFile(templateFile, File(backupFilePath))
        logInfo("已备份文件到：${backupFilePath}")
    }

    fun syncTable(context: GeneratorContext) {
        if (!generatorConfig.syncTable) {
            return
        }
        val sqlFilePath = getSqlFilePath(context.classModel)
        context.sqlFilePath = sqlFilePath
        dbConnector.syncTable(context, connection)
    }

    fun writeLog(context: GeneratorContext) {
        if (!generatorConfig.generateLog) {
            // INFO: Zhouwx: 2025/8/18 不生成日志文件，则直接返回
            return
        }
        val logFilePath = context.logFilePath
        val classModel = context.classModel
        val logFile = File(logFilePath)
        if (!logFile.exists()) {
            logFile.createNewFile()
        } else {
            // INFO: Zhouwx: 2025/8/15 防止日志文件过大
            val readString = FileUtil.readString(logFile, Charset.defaultCharset())
            var lines = readString.split("\n")
            if (lines.size > generatorConfig.logRecordCount) {
                val deleteCount = lines.size - (generatorConfig.logRecordCount)
                lines = lines.subList(deleteCount, lines.size)
                val resultContent = lines.joinToString("\n")
                logFile.writeText(resultContent)
            }
        }
        if (context.overwrite) {
            // INFO: Zhouwx: 2025/8/18 如果强制刷新，则重写日志文件
            logFile.writeText(JSON.toJSONString(classModel))
        } else {
            if (context.haveChange) {
                // INFO: Zhouwx: 2025/8/18 如果是增量更新，则是追加一行日志信息
                FileUtil.appendString("\n" + JSON.toJSONString(classModel), logFile, Charset.defaultCharset())
            } else {
                // INFO: Zhouwx: 2025/9/11 没有修改，则不生成日志信息
            }
        }
    }

    fun getLogFilePath(classModel: ClassModel): String {
        val resourcesPath = getResourcesPath(classModel)
        val logFolderPath = "${resourcesPath}/generator/log"
        File(logFolderPath).mkdirs()
        val logFilePath = "${logFolderPath}/${classModel.fullClassName}.log"
        return logFilePath
    }

    private fun getSqlFilePath(classModel: ClassModel): String {
        val resourcesPath = getResourcesPath(classModel)
        val sqlFolderPath = "${resourcesPath}/generator/sql"
        File(sqlFolderPath).mkdirs()
        val sqlFilePath = "${sqlFolderPath}/update-${generatorConfig.sessionVersion}.sql"
        if (!generatorConfig.generateSql) {
            return sqlFilePath
        }
        val file = File(sqlFilePath)
        if (!file.exists()) {
            file.createNewFile()
        }
        return sqlFilePath
    }

    private fun getResourcesPath(classModel: ClassModel): String {
        var rootPath = generatorConfig.groupModulePath[GroupEnum.BACKEND] ?: throw RuntimeException("请配置后端模块路径")
        rootPath = rootPath.replace("\\", "/")
        if (!rootPath.endsWith("/")) {
            rootPath += "/"
        }
        val split = rootPath.split("/")
        val moduleName = split[split.size - 2]
        val resourcePath = "${rootPath}/${moduleName}-entity/src/main/resources"
        return resourcePath
    }

    fun writeEntityFileUuid(context: GeneratorContext) {
        val classModel = context.classModel

        val entityFile = classModel.entityFile
        var entityFileContent = FileUtil.readString(entityFile, Charset.defaultCharset())
        if (entityFileContent.contains("// [[ OnflowGenerator Entity UUID:")) {
            context.firstGenerate = false
            return
        }

        context.firstGenerate = true
        val uuid = classModel.fileUuid
        entityFileContent = "// [[ OnflowGenerator Entity UUID: ${uuid} ]]\n" + entityFileContent
        FileUtil.writeString(entityFileContent, entityFile, Charset.defaultCharset())
    }

    fun normalizeEmptyLines(code: String): String {
        val lines = code.lineSequence().toMutableList()
        val result = mutableListOf<String>()
        var emptyLineCount = 0

        for (line in lines) {
            if (line.isBlank()) {
                emptyLineCount++
                // 只在连续空行的第一个位置保留一个空行
                if (emptyLineCount == 1) {
                    result.add("")
                }
            } else {
                // 遇到非空行时重置计数器并保留原行内容
                emptyLineCount = 0
                result.add(line)
            }
        }

        return result.joinToString("\n")
    }

    fun baseTypeToPackageType(baseType: String): String {
        val packageType = when (baseType) {
            "long" -> "java.lang.Long"
            "int" -> "java.lang.Integer"
            "double" -> "java.lang.Double"
            "float" -> "java.lang.Float"
            "boolean" -> "java.lang.Boolean"
            "short" -> "java.lang.Short"
            "char" -> "java.lang.Character"
            "byte" -> "java.lang.Byte"
            "String" -> "java.lang.String"
            "string" -> "java.lang.String"
            else -> baseType
        }
        return packageType
    }
}